// /////////////////////////////////////////////////////////////////////////////
//
// Name:            ScriptProcessor.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           AssetProcessor for processing scripts in an AssetGroup.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "ScriptProcessor.h"

#ifdef __COMPILE_WITH_ANGELSCRIPT__

#include "../core/GameManager.h"


// ScriptProcessor class
// ScriptProcessor constructor.
ScriptProcessor::ScriptProcessor(AssetGroup *parent)
: AssetProcessor(parent, "scripts"), mIsReloading(false)
{
}

// ScriptProcessor deconstructor.
ScriptProcessor::~ScriptProcessor()
{
    removeAssets();
}

// Gets the script with the given filename.
Script* ScriptProcessor::getScript(const std::string &fileName)
{
    // Did we already instanciate the script?
#ifdef WIN32
	std::string tmp = fileName;
	std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
	std::map<std::string, Script*>::iterator itScripts = mScripts.find(tmp);
#else
	std::map<std::string, Script*>::iterator itScripts = mScripts.find(fileName);
#endif

    if(itScripts != mScripts.end())
      return itScripts->second;

    // Do we have the file in memory so that we can instanciate the script from there?
#ifdef WIN32
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles = mScriptFiles.find(tmp);
#else
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles = mScriptFiles.find(fileName);
#endif

    if(itFiles != mScriptFiles.end())
    {
        // Retrieve pointers to sub-sytems of the Irrlicht engine.
        IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

        // Create a memory read file.
        long fSize = (itFiles->second).second;
        c8 *fBuffer = (itFiles->second).first;

        IReadFile *file = fileSystem->createMemoryReadFile(fBuffer, fSize, fileName.c_str());

        // Create the script.
        Script *script = GameManager::Instance()->getScriptManager()
                           ->createScriptFromFile(file, false);

        mScripts[fileName] = script;
        script->grab();

        // Clean up.
        file->drop();

        // Return the script.
        return script;
    }

    // We didn't find anything.
    return NULL;
}

// Loads the mesh with the given filename.
bool ScriptProcessor::loadAsset(const std::string &fileName)
{
    // Retrieve pointers to sub-sytems of the Irrlicht engine.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Change the working directory to the scripts directory.
    std::string workingDir = (fileSystem->getWorkingDirectory()).c_str();

    if(!fileSystem->changeWorkingDirectoryTo( (pParent->getBaseDir()).c_str() ) )
      return false;

    if(!fileSystem->changeWorkingDirectoryTo("./scripts/"))
      return false;

    // Load the script.
    IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

    if(file)
    {
       // Retrieve data.
       long fSize = file->getSize();
       c8 *fBuffer = new c8[fSize+1];
       memset(fBuffer, 0, fSize+1);

       file->read(fBuffer, fSize);

       // Store data.

       mScriptFiles[fileName] = std::pair<c8*, long>(fBuffer, fSize);
       cout << "Loaded script: " << fileName << "\n";

       // Handle reload.
       if(mIsReloading)
       {
#ifdef WIN32
		   std::string tmp = fileName;
		   std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
           std::map<std::string, Script*>::iterator it = mScripts.find(tmp);
#else
           std::map<std::string, Script*>::iterator it = mScripts.find(fileName);
#endif

           if(it != mScripts.end())
           {
               file->seek(0);

               Script *script = it->second;
               script->unloadScript();
               script->loadScript(file);
           }
       }

       // Clean up.
       file->drop();
    }

    // Clean up.
    fileSystem->changeWorkingDirectoryTo(workingDir.c_str());

    return (file) ? true : false;
}

// Loads the meshes associated with this asset group.
void ScriptProcessor::loadAssets()
{
    // Retrieve pointers to sub-sytems of the Irrlicht engine.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Change the working directory to the textures directory.
    std::string workingDir = (fileSystem->getWorkingDirectory()).c_str();

    if(!fileSystem->changeWorkingDirectoryTo( (pParent->getBaseDir()).c_str() ) )
      return;

    if(!fileSystem->changeWorkingDirectoryTo("./scripts/"))
      return;

    // Read the scripts.
    IFileList *fileList = fileSystem->createFileList();

    for(u32 i = 0; i < fileList->getFileCount(); i++)
    {
        // Get file name.
        std::string fileName = (fileList->getFileName(i)).c_str();

        if(fileName == ".." || fileName == ".")
          continue;

        // Load the script.
        IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

        if(file)
        {
           // Retrieve data.
           long fSize = file->getSize();
           c8 *fBuffer = new c8[fSize+1];
           memset(fBuffer, 0, fSize+1);

           file->read(fBuffer, fSize);

           // Store data.
           mScriptFiles[fileName] = std::pair<c8*, long>(fBuffer, fSize);
           cout << "Loaded script: " << fileName << "\n";

           // Handle reload.
           if(mIsReloading)
           {
#ifdef WIN32
			  std::string tmp = fileName;
			  std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
              std::map<std::string, Script*>::iterator it = mScripts.find(tmp);
#else
              std::map<std::string, Script*>::iterator it = mScripts.find(fileName);
#endif

              if(it != mScripts.end())
              {
                 file->seek(0);

                 Script *script = it->second;
                 script->unloadScript();
                 script->loadScript(file);
              }
           }

           // Clean up.
           file->drop();
        }
    }

    // Clean up.
    fileList->drop();

    fileSystem->changeWorkingDirectoryTo(workingDir.c_str());
}

// Reloads the asset with the given filename.
bool ScriptProcessor::reloadAsset(const std::string &fileName)
{
    mIsReloading = true;

    removeAsset(fileName);
    bool succeeded = loadAsset(fileName);

    mIsReloading = false;

    return succeeded;
}

// Reloads all assets.
void ScriptProcessor::reloadAssets()
{
    // We do a regular reload.
    mIsReloading = true;

    removeAssets();
    loadAssets();

    mIsReloading = false;

    // We need to resync our scripts map.
    // Create a new map with all existing scripts.
    std::map<std::string, Script*> newMap;

    std::map<std::string, std::pair<c8*, long> >::iterator itFiles;
    for(itFiles = mScriptFiles.begin(); itFiles != mScriptFiles.end(); itFiles++)
    {
        std::map<std::string, Script*>::iterator itScripts = mScripts.find(itFiles->first);

        if(itScripts != mScripts.end())
        {
            newMap[itScripts->first] = itScripts->second;
            mScripts.erase(itScripts);
        }
    }

    // Remove redundant scripts.
    std::map<std::string, Script*>::iterator itScripts;
    for(itScripts = mScripts.begin(); itScripts != mScripts.end(); itScripts++)
       (itScripts->second)->drop();

    mScripts.clear();

    // Set new map.
    mScripts = newMap;
}

// Removes the asset with the given filename.
bool ScriptProcessor::removeAsset(const std::string &fileName)
{
    // Predeclaration of succeeded variable.
    bool succeeded = false;

    // Try to find the given script file.
#ifdef WIN32
	std::string tmp = fileName;
	std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
    std::map<std::string, std::pair<c8*, long> >::iterator it = mScriptFiles.find(tmp);
#else
    std::map<std::string, std::pair<c8*, long> >::iterator it = mScriptFiles.find(fileName);
#endif

    // Did we find the script file?
    if(it != mScriptFiles.end())
    {
       // Delete the script file.
       c8 *fBuffer = (it->second).first;
       delete [] fBuffer;
       mScriptFiles.erase(it);

       succeeded = true;
    }

    // Try to find the given script, but only if we're not reloading.
    if(succeeded && !mIsReloading)
    {
#ifdef WIN32
       std::map<std::string, Script*>::iterator it = mScripts.find(tmp);
#else
       std::map<std::string, Script*>::iterator it = mScripts.find(fileName);
#endif

       // Did we find the script?
       if(it != mScripts.end())
       {
          (it->second)->drop();
          mScripts.erase(it);
       }
    }

    // Return whether we succeeded or not.
    return succeeded;
}

// Removes all asssets.
void ScriptProcessor::removeAssets()
{
    // Delete the script files.
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles;

    for(itFiles = mScriptFiles.begin(); itFiles != mScriptFiles.end(); itFiles++)
    {
       // Delete the script file.
       c8 *fBuffer = (itFiles->second).first;
       delete [] fBuffer;
    }

    // Clear the script files map.
    mScriptFiles.clear();

    // Remove the scripts, but only if we're not reloading.
    if(!mIsReloading)
    {
       std::map<std::string, Script*>::iterator itScripts;

       for(itScripts = mScripts.begin(); itScripts != mScripts.end(); itScripts++)
         (itScripts->second)->drop();

       // Clear the scripts map.
       mScripts.clear();
    }
}

// Creates a new ScriptProcessor.
AssetProcessor* ScriptProcessor::createProcessor(AssetGroup *parent)
{
    return new ScriptProcessor(parent);
}

#endif // __COMPILE_WITH_ANGELSCRIPT__

// End of File
